/* Copyright (C) 2011 altered version of Ivan Tikhonov by 
   NAIT-ABDELAZIZ Ameziane and LATHUILIERE Bruno.
   
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
   arising from the use of this software.
   
   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:
   
   1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
   2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
   
   Ivan Tikhonov, kefeer@brokestream.com   
   Ameziane Nait abdelaziz naitabde@gmail.com
*/


/*******************************************************************************/

#include <pthread.h>
//#include <execinfo.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <assert.h>
#include <sys/mman.h>//pour mmap
#include <sys/stat.h>//pour les file discriptor
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>
/*------------------------------------------------------------------------------*/




#include <sys/types.h>//recuperer le tid du thread

/*Code d'instrumentation*/
//#include "back_trace.h"

/*-----------------------------------------------------------------------------*/
#include <errno.h>
#include <fcntl.h>
/*-----------------------------------------------------------------------------*/


#ifndef __USE_GNU
#define __USE_GNU
#endif

/*-----------------------------------------------------------------------------*/
#include <dlfcn.h>
/*-----------------------------------------------------------------------------*/
#include "log-malloc.h"

#define ADDRESSE_T  unsigned long
#define SELF       (unsigned int) pthread_self()


/**
 *\brief Structure regroupant les pointeurs vers les vrais fonctions
 *d'allocation dans le tas, ie les fonctions de la famille malloc.
 */
static log_malloc_handle malloc_ptrs;
/*-----------------------------------------------------------------------------*/

/**
 *\brief Structure de données permettant de regrouper quelques infos sur 
 *les appels de fonction realises.
 */
static alloc_stats stats;
/*-----------------------------------------------------------------------------*/



/**
 *\brief Flag indiquant si les pointeurs sont biens initialisés
 */
static int initialised =0;
/*----------------------------------------------------------------------------*/


/**
 *\brief Flag indiquant si on a fait appel à dlsym
 */
static int from_dlsym  =0;
/*---------------------------------------------------------------------------*/

/**
 *\brief Compte le nombre d'appel à calloc faits par la libdl
 */
static int compteur_allocalloc=0;
/*--------------------------------------------------------------------------*/




/**
 *\brief Utilisé pour définir le pool de mémoire utilisé en interne 
 * pour satisfaire certaines requetes de calloc issues de la libdl
 */
#define PAGE_SIZE 4096
#define NB_PAGES  100
/*--------------------------------------------------------------------------*/

/**
 *buffer fixe où piocher pour satisfaire la requete calloc, 
 *100 pages en l'occurence dans le BSS.
 */
static char mem_pool[NB_PAGES][PAGE_SIZE];
/*--------------------------------------------------------------------------*/

/**
 *brief Fonction qui alloue de la mémoire pour calloc issu de la libdl
 */
static void* allocate_to_calloc(size_t nblk,size_t size)
{
  void* mem = mem_pool[compteur_allocalloc];
  nblk++;
  size++;
#ifdef DEBUG
  printf("<nblk,%d>,<size,%d>,<count:%d>\n",nblk,size,compteur_allocalloc);
#endif
  
  compteur_allocalloc++;
  return mem;
}
/*-----------------------------------------------------------------------------*/



/**
 *brief Fonction qui libère la mémoire alloué par calloc issu de la libdl
 */
static void free_from_dlsym(void* ptr)
{
 if(ptr == NULL)
 printf("memory corruption, trying to free a NULL pointer\n");
 assert(ptr!=NULL);
 
#ifdef DEBUG
  printf("free from our pool\n");
#endif
}
/*-----------------------------------------------------------------------------*/




/**
 *\brief Initialise la structure de données stats.
 */
static inline void initStats()
{
  printf("Initialisation des statistiques\n");
  stats.memory_allocation=0;
  stats.compteur_malloc=0;
  stats.compteur_calloc=0;
  stats.compteur_free=0;
  stats.size=0;
  stats.current_addr=NULL;
  stats.compteur=0;
  stats.nmemb=0;
  printf("Fin de l'initialisation des statistiques\n");
}
/*-------------------------------------------------------------------------------*/

/**
 *\brief Lie les bonnes fonctions d'allocation aux pointeurs.
 */
inline static void initMallocPtrs()
{
#ifdef DEBUG
  printf("Initialisation des pointeurs d'allocation\n");
#endif
  
  /*Linkage dynamique de la bibliothèque standard*/
  MAC_DESACTIVATE_LOG();
  
  /*on met le drapeau pour dire que dlsym est appelé*/
  from_dlsym = 1;

  
  malloc_ptrs.real_calloc = (void * (*)(size_t nmemb, size_t size)) dlsym(RTLD_NEXT, "calloc");
  malloc_ptrs.real_malloc = (void * (*)(size_t size)              ) dlsym(RTLD_NEXT, "malloc"); 
  malloc_ptrs.real_free   = (void   (*)(void*)                    ) dlsym(RTLD_NEXT, "free"  );
  malloc_ptrs.real_realloc= (void * (*)(void* ptr,size_t size))     dlsym(RTLD_NEXT,"realloc");
  
  /*si le linkage foire on quitte, merci de votre essai*/
  assert(malloc_ptrs.real_malloc !=NULL);
  assert(malloc_ptrs.real_calloc !=NULL);
  assert(malloc_ptrs.real_free   !=NULL);
  assert(malloc_ptrs.real_realloc!=NULL);

#ifdef DEBUG
  printf("Fin de l'initialisation des pointeurs d'allocation\n");
#endif
  
  MAC_ACTIVATE_LOG();
  /*on eleve le drapeau*/
  from_dlsym = 0;
  
}
/*******************************************************************************************/


/**
 *Initialisation du systeme de log
 */
static void init_me()
{
#ifdef DEBUG
  printf("Dans init_me() with thread %d\n",SELF); 
#endif
  
  /*desactive le système de log*/
  MAC_DESACTIVATE_LOG();
  
  /*initialise les pointeurs d'allocation*/
  initMallocPtrs();
  
  /*signale que c'est initialisé*/
  initialised=1;
  
  /*initialise les statistiques communes*/
  stats.memory_allocation = 0;
  stats.compteur++;
  
  /*On réactive le système de log*/
  MAC_ACTIVATE_LOG();
}
/********************************************************************************************/




/*
 *Histoire d'avoir une fonction qui affiche le rapport
 */

alloc_stats get_memory_summary()
{
  alloc_stats st;
  st.memory_allocation=memory_allocation;
  st.compteur_malloc  =compteur_malloc;
  st.compteur_calloc  =compteur_calloc;
  st.compteur_free    =compteur_free;
  st.compteur_realloc =compteur_realloc;
  
  st.size=0;
  st.current_addr=NULL;
  fflush(stdout);
  printf("--memory summary--\n");
  printf("malloc  calls count: %d\n",compteur_malloc  );
  printf("calloc  calls count: %d\n",compteur_calloc  );
  printf("realloc calls count: %d (in place:%d),(dec:%d)\n",
	 compteur_realloc,compteur_realloc_ip,compteur_realloc_dec );
  printf("free   calls count: %d\n" ,compteur_free    );
  printf("total memory allocated from malloc interface : %d KB\n",(int)memory_allocation/1024);
  printf("------------------\n");
  return st;
  
}



/**
 *Fermeture du systeme de log
 */
void finalize_me()
{
  /*Desactive le système de log*/
  MAC_DESACTIVATE_LOG();
  
  /*deteruit le mutex utilisé*/
#ifdef WITH_PTHREADS
  //pthread_mutex_destroy(&loglock);
#endif
}
/*********************************************************************************************/

#define PRIORITY 1000

/**
 *Initialisation de la lib
 */
void __attribute__((constructor)) log_init (void)
{
  printf("Initialisation de la bibliothèque d'interposition\n");
  initStats();
  
  LOCK;
  init_me();
  UNLOCK;
  
  printf("Fin de l'initialisation de la bibliothèque\n");
}
/*********************************************************************************************/

/**
 *Finitialisation de la lib
 */
void __attribute__((destructor)) log_finalize (void)
{
  printf("Finalisation de la bibliothèque d'interposition\n");
  finalize_me();
  get_memory_summary();
}
/*-------------------------------------------------------------------------------------------*/


/**
 *Surcharge du malloc
 */
void *malloc(size_t size)
{
#ifdef DEBUG
  printf("Je rentre dans un malloc avec le thread %d\n",SELF); 
#endif
  /*va contenir le resultat*/
  void * result;
  
  
  LOCK;//Ce lock pose des problèmes avec certains codes (exemple ls)
  if (initialised != 1){
    MAC_DESACTIVATE_LOG();
    init_me();
#ifdef DEBUG
    printf("initialised %i\n",initialised);
#endif
  }
  
  if(MAC_LOG_ACTIVATED()) {           
    result=malloc_ptrs.real_malloc(size);
    compteur_malloc++;
    memory_allocation+=size;
    stats.current_addr=result;
    stats.size=size;
    
    /*Desactiver l'interception*/
    MAC_DESACTIVATE_LOG();
    {
      //Ici placer le code de traitement de l'interception
    }
    /*Reactiver l'interception*/
    MAC_ACTIVATE_LOG();
    
    UNLOCK;
    /*FIN DE SECTION CRITIQUE*/
#ifdef DEBUG
    printf("retour du malloc = %p\n",result);
#endif 
    
    return result;
  }   
  else
    {
      result=malloc_ptrs.real_malloc(size);
      UNLOCK;
#ifdef DEBUG
      printf("retour de malloc\n");
#endif
      return result;
    }
}
/*----------------------------------------------------------------------------------------------*/



/**
 *\brief permet de mettre à jour les stats de calloc
 */
static inline void update_calloc_stats(size_t nmemb,size_t size)
{
  compteur_calloc++;
  memory_allocation+=nmemb*size;
  stats.size=size;
  stats.nmemb=nmemb;
}
/*----------------------------------------------------------------------------------------------*/


/**
 *Surcharge du calloc
 */
void *calloc(size_t nmemb, size_t size)
{
#ifdef DEBUG
  printf("Je rentre dans un calloc avec le thread %d\n",SELF);
#endif
  
  void* res;
  
  /*protection des deux test et des pointeurs d'allocation*/
  
  if (from_dlsym == 1){
#ifdef DEBUG
    printf("Je rentre dans un calloc issu de dlsym\n");
#endif
    res=allocate_to_calloc(nmemb,size);
    return res;
  }
#ifdef DEBUG
  printf("Locking calloc thread: %d\n",SELF);
#endif
  
  LOCK;
  if (initialised != 1){
    MAC_DESACTIVATE_LOG();
    init_me();
    
#ifdef DEBUG
    printf("initialised %i\n",initialised);
#endif
  }
  
  /*Mise à jour des statistiques*/
  update_calloc_stats(nmemb,size);
  
  if(MAC_LOG_ACTIVATED()){ 
    res=malloc_ptrs.real_calloc(nmemb,size);
    stats.current_addr=res;
    //log
#ifdef DEBUG
    printf("Unlock thread %d\n",SELF);
#endif
    UNLOCK;
    return res;
  }
  else{
#ifdef DEBUG
    printf("Logging is not activated %d from calloc =%p\n",SELF,malloc_ptrs.real_calloc);
#endif
    void* p =NULL;
    p=malloc_ptrs.real_calloc(nmemb,size);
    
#ifdef DEBUG
    printf("Sortie de calloc %d\n",SELF);
#endif
    
    UNLOCK;
    return p;
  }
}
/*************************************************************************************************/


/**
 *Surcharge du free
 */
void free(void *ptr)
{
  
  /*case 1*/
  if(ptr == NULL){
    return ;
  }
  /*case 2*/
  if (from_dlsym == 1){
    
#ifdef DEBUG
    assert(0);
#endif
    
    free_from_dlsym(ptr); 
    return ;
  }
  
  /*case 3*/
  LOCK;
  compteur_free++;
#ifdef DEBUG
  printf("Je rentre dans un free <ptr:%p,count:%d>\n",ptr,stats.compteur_free);
#endif
  {
    if(MAC_LOG_ACTIVATED()){ 
      
      MAC_DESACTIVATE_LOG();
      {
	//Ici invoquer le code qui traite l'interception
#ifdef DEBUG
	printf("FREE .....\n");
#endif
      }
      MAC_ACTIVATE_LOG(); 
    }
  }
  malloc_ptrs.real_free(ptr);
#ifdef DEBUG
  printf("true free called with = %p, count:%d, thread:%d\n",ptr,stats.compteur_free,SELF);
#endif
  UNLOCK;
}
/*********************************************************************************************/

/*
 *\brief Surcharge du realloc
 **/
void *realloc(void *ptr, size_t size)
{ 
#ifdef DEBUG
  printf("Je rentre dans un calloc avec le thread %d\n",SELF); 
#endif
  /*va contenir le resultat*/
  void * result;
  
  
  LOCK;
  
  if (initialised != 1){
    MAC_DESACTIVATE_LOG();
    init_me();
#ifdef DEBUG
    printf("initialised %i\n",initialised);
#endif
  }
  
  if(MAC_LOG_ACTIVATED()) {           
    result=malloc_ptrs.real_realloc(ptr,size);
    compteur_realloc++;
    
    /*incrementer le nombre de realloc sur place*/
    if(result==ptr)
      compteur_realloc_ip++;
    else
      compteur_realloc_dec++;
    
    /*mise à jour des stats globales*/
    memory_allocation+=size;
    stats.current_addr=result;
    stats.size=size;
    
    /*Desactiver l'interception*/
    MAC_DESACTIVATE_LOG();
    {
      //Ici placer le code de traitement de l'interception
    }
    
    
    /*Reactiver l'interception*/
    MAC_ACTIVATE_LOG();
    
    UNLOCK;
    /*FIN DE SECTION CRITIQUE*/
#ifdef DEBUG
    printf("retour du realloc = %p\n",result);
#endif 
    
    return result;
  }   
  else
    {
      result=malloc_ptrs.real_realloc(ptr,size);
      UNLOCK;
#ifdef DEBUG
      printf("retour de calloc\n");
#endif
      return result;
    }
}
